package com.fingard.dsp.bank.directbank.abc02.util;

import javax.crypto.Cipher;

import com.fingard.io.FileHelper;
import com.fingard.text.Base64Helper;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Date;

/**
 * Created by F on 2019/5/9.
 */
public class RsaHelper {

    /** *//**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /** *//**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    /**
     * 生成RSA密钥对(默认密钥长度为1024)
     *
     * @return
     */
    public static KeyPair generateRSAKeyPair() {
        return generateRSAKeyPair(1024);
    }

    /**
     * 生成RSA密钥对
     *
     * @param keyLength
     *            密钥长度，范围：512～2048
     * @return
     */
    public static KeyPair generateRSAKeyPair(int keyLength) {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(keyLength);
            return kpg.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
    }

    public static String encodePublicKeyToXml(PublicKey key) {
        if (!RSAPublicKey.class.isInstance(key)) {
            return null;
        }
        RSAPublicKey pubKey = (RSAPublicKey) key;
        StringBuilder sb = new StringBuilder();

        sb.append("<RSAKeyValue>");
        sb.append("<Modulus>")
                .append(Base64Helper.encode(pubKey.getModulus().toByteArray()))
                .append("</Modulus>");
        sb.append("<Exponent>")
                .append(Base64Helper.encode(pubKey.getPublicExponent()
                        .toByteArray())).append("</Exponent>");
        sb.append("</RSAKeyValue>");
        return sb.toString();
    }



    public static PublicKey decodePublicKeyFromXml(String xml) {
        xml = xml.replaceAll("\r", "").replaceAll("\n", "");
        BigInteger modulus = new BigInteger(1, Base64Helper.decode(StringUtils
                .GetMiddleString(xml, "<Modulus>", "</Modulus>")));
        BigInteger publicExponent = new BigInteger(1,
                Base64Helper.decode(StringUtils.GetMiddleString(xml,
                        "<Exponent>", "</Exponent>")));

        RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus,
                publicExponent);

        KeyFactory keyf;
        try {
            keyf = KeyFactory.getInstance("RSA");
            return keyf.generatePublic(rsaPubKey);
        } catch (Exception e) {
            return null;
        }
    }

    public static PublicKey decodePublicKeyFromFile(String filepath){
        String xml = FileHelper.readFileText(filepath,"gbk");
        return decodePublicKeyFromXml(xml);
    }

    public static PrivateKey decodePrivateKeyFromXml(String xml) {
        xml = xml.replaceAll("\r", "").replaceAll("\n", "");
        BigInteger modulus = new BigInteger(1, Base64Helper.decode(StringUtils
                .GetMiddleString(xml, "<Modulus>", "</Modulus>")));
        BigInteger publicExponent = new BigInteger(1,
                Base64Helper.decode(StringUtils.GetMiddleString(xml,
                        "<Exponent>", "</Exponent>")));
        BigInteger privateExponent = new BigInteger(1,
                Base64Helper.decode(StringUtils.GetMiddleString(xml, "<D>",
                        "</D>")));
        BigInteger primeP = new BigInteger(1, Base64Helper.decode(StringUtils
                .GetMiddleString(xml, "<P>", "</P>")));
        BigInteger primeQ = new BigInteger(1, Base64Helper.decode(StringUtils
                .GetMiddleString(xml, "<Q>", "</Q>")));
        BigInteger primeExponentP = new BigInteger(1,
                Base64Helper.decode(StringUtils.GetMiddleString(xml, "<DP>",
                        "</DP>")));
        BigInteger primeExponentQ = new BigInteger(1,
                Base64Helper.decode(StringUtils.GetMiddleString(xml, "<DQ>",
                        "</DQ>")));
        BigInteger crtCoefficient = new BigInteger(1,
                Base64Helper.decode(StringUtils.GetMiddleString(xml,
                        "<InverseQ>", "</InverseQ>")));

        RSAPrivateCrtKeySpec rsaPriKey = new RSAPrivateCrtKeySpec(modulus,
                publicExponent, privateExponent, primeP, primeQ,
                primeExponentP, primeExponentQ, crtCoefficient);

        KeyFactory keyf;
        try {
            keyf = KeyFactory.getInstance("RSA");
            return keyf.generatePrivate(rsaPriKey);
        } catch (Exception e) {
            return null;
        }
    }

    public static PrivateKey decodePrivateKeyFromFile(String filepath){
        String xml = FileHelper.readFileText(filepath,"gbk");
        return decodePrivateKeyFromXml(xml);
    }

    public static String encodePrivateKeyToXml(PrivateKey key) {
        if (!RSAPrivateCrtKey.class.isInstance(key)) {
            return null;
        }
        RSAPrivateCrtKey priKey = (RSAPrivateCrtKey) key;
        StringBuilder sb = new StringBuilder();

        sb.append("<RSAKeyValue>");
        sb.append("<Modulus>")
                .append(Base64Helper.encode(priKey.getModulus().toByteArray()))
                .append("</Modulus>");
        sb.append("<Exponent>")
                .append(Base64Helper.encode(priKey.getPublicExponent()
                        .toByteArray())).append("</Exponent>");
        sb.append("<P>")
                .append(Base64Helper.encode(priKey.getPrimeP().toByteArray()))
                .append("</P>");
        sb.append("<Q>")
                .append(Base64Helper.encode(priKey.getPrimeQ().toByteArray()))
                .append("</Q>");
        sb.append("<DP>")
                .append(Base64Helper.encode(priKey.getPrimeExponentP()
                        .toByteArray())).append("</DP>");
        sb.append("<DQ>")
                .append(Base64Helper.encode(priKey.getPrimeExponentQ()
                        .toByteArray())).append("</DQ>");
        sb.append("<InverseQ>")
                .append(Base64Helper.encode(priKey.getCrtCoefficient()
                        .toByteArray())).append("</InverseQ>");
        sb.append("<D>")
                .append(Base64Helper.encode(priKey.getPrivateExponent()
                        .toByteArray())).append("</D>");
        sb.append("</RSAKeyValue>");
        return sb.toString();
    }

    // 用公钥加密
    public static byte[] encryptData(byte[] data, PublicKey pubKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            int inputLen = data.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = out.toByteArray();
            out.close();
            return encryptedData;
        } catch (Exception e) {
            return null;
        }
    }

    // 用私钥解密
    public static byte[] decryptData(byte[] encryptedData, PrivateKey priKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();
            return decryptedData;
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * 根据指定私钥对数据进行签名(默认签名算法为"SHA1withRSA")
     *
     * @param data
     *            要签名的数据
     * @param priKey
     *            私钥
     * @return
     */
    public static byte[] signData(byte[] data, PrivateKey priKey) {
        return signData(data, priKey, "SHA1withRSA");
    }

    /**
     * 根据指定私钥和算法对数据进行签名
     *
     * @param data
     *            要签名的数据
     * @param priKey
     *            私钥
     * @param algorithm
     *            签名算法
     * @return
     */
    public static byte[] signData(byte[] data, PrivateKey priKey,
                                  String algorithm) {
        try {
            Signature signature = Signature.getInstance(algorithm);
            signature.initSign(priKey);
            signature.update(data);
            return signature.sign();
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * 用指定的公钥进行签名验证(默认签名算法为"SHA1withRSA")
     *
     * @param data
     *            数据
     * @param sign
     *            签名结果
     * @param pubKey
     *            公钥
     * @return
     */
    public static boolean verifySign(byte[] data, byte[] sign, PublicKey pubKey) {
        return verifySign(data, sign, pubKey, "SHA1withRSA");
    }

    /**
     *
     * @param data 数据
     * @param sign 签名结果
     * @param pubKey 公钥
     * @param algorithm 签名算法
     * @return
     */
    public static boolean verifySign(byte[] data, byte[] sign,
                                     PublicKey pubKey, String algorithm) {
        try {
            Signature signature = Signature.getInstance(algorithm);
            signature.initVerify(pubKey);
            signature.update(data);
            return signature.verify(sign);
        } catch (Exception ex) {
            return false;
        }
    }

    public static void main(String[] args)throws Exception {
        String file = "C:\\Users\\F\\Desktop\\农行查询加密文件201905150946\\030200697402--密文";
        decrypt(file);

//        KeyPair kp = RsaHelper.generateRSAKeyPair();
//        PublicKey pubKey = kp.getPublic();
//        PrivateKey priKey = kp.getPrivate();
//
//        String pubKeyXml = RsaHelper.encodePublicKeyToXml(pubKey).replaceAll("\r|\n", "");
//        String priKeyXml = RsaHelper.encodePrivateKeyToXml(priKey).replaceAll("\r|\n", "");
//        System.out.println("====公钥====");
//        System.out.println("公钥长度："+pubKeyXml.length());
//        System.out.println(pubKeyXml);
//        String cer = "E:\\rsa"+ File.separator+"pub.cer";
//        FileHelper.writeFile(cer, pubKeyXml, "gbk");
////
//        System.out.println("====私钥====");
//        System.out.println("私钥长度："+priKeyXml.length());
//        System.out.println(priKeyXml);
//        String key = "E:\\rsa"+ File.separator+"pri.key";
//        FileHelper.writeFile(key, priKeyXml, "gbk");
        String pubKeyXml =FileHelper.readFileText("E:\\rsa\\农行客户密钥5.15\\test_cus_pub.cer","gbk");
        String priKeyXml =FileHelper.readFileText("E:\\rsa\\农行客户密钥5.15\\test_cus_pri.key","gbk");

        PublicKey pubKey2 = RsaHelper.decodePublicKeyFromXml(pubKeyXml);
        PrivateKey priKey2 = RsaHelper.decodePrivateKeyFromXml(priKeyXml);

        String pubKeyXml2 = RsaHelper.encodePublicKeyToXml(pubKey2);
        String  priKeyXml2 = RsaHelper.encodePrivateKeyToXml(priKey2);
        System.out.println("原来的私钥:" +priKeyXml);
        System.out.println("转换后的私钥："+priKeyXml2);
        System.out.println(priKeyXml.equals(pubKeyXml2));
//
//        System.out.println("====公钥对比====");
//        System.out.println(pubKey.toString());
//        System.out.println("------");
//        System.out.println(pubKey2.toString());
//
//        System.out.println("====私钥对比====");
//        System.out.println(priKey.toString());
//        System.out.println("------");
//        System.out.println(priKey2.toString());

        try {
//            String pubKeyXml3 = "<RSAKeyValue><Modulus>rHESyuI3ny4MLsqDBalW9ySaodCL0e6Bsrl01Q5G1qm2wjUoGULazZSNqZY+JQNjU92tW3Snk5RPIkv+wDj+uOT9LTUjQImltHnzqMvbt06GipVXDOyBLTa7G/zRIe/CrjyJ+XEYX2xIhpe5ayowl3HHUpZ71jRNioyxaVVZ8S0=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
//            String priKeyXml3 = "<RSAKeyValue><Modulus>rHESyuI3ny4MLsqDBalW9ySaodCL0e6Bsrl01Q5G1qm2wjUoGULazZSNqZY+JQNjU92tW3Snk5RPIkv+wDj+uOT9LTUjQImltHnzqMvbt06GipVXDOyBLTa7G/zRIe/CrjyJ+XEYX2xIhpe5ayowl3HHUpZ71jRNioyxaVVZ8S0=</Modulus><Exponent>AQAB</Exponent><P>5a7uM+IeY8QMVQl0q88ZTqWbB555l7+366cUIClTN8z2ZXzTnWFCNoQzUrG14FouJFYumFZD12Ni5MkJK6gqSw==</P><Q>wDMhwwO4kz82uSG+FlCBr06fYk2COTg0TofmSp/5OrVqgkBIe7FgpTpVGzGLk0mvOLcy6UZftq//W0Saow6nZw==</Q><DP>FbjDgliiMyE5YVlxlUYSyKNU1BWivj09caXte1UtL5vMubBiewHVtz4tdGamIr+kmX8lDPcrl1Uo5yY0HdLbnQ==</DP><DQ>kIjjJsgxkWnEOUyKqjU4kSDK8x3ehDEkBLpmEFBlGCU9R14YJAyr5RUM0zpbABQ1VK1P9+UYLUYE/hmFQIHQmQ==</DQ><InverseQ>pxQDThwSnUZ4EaNaCPl1ovYypdQUZaZ/Sld1+0n8FEjkmRcGP1R9VMuj1ViPZg3rvm2GeP8Xv1SJqJUVueWiGA==</InverseQ><D>DxBNoPWEAF7IZ6n/KhZx52MGMw6BuFQKdm9m+lml7Iik03BLUXGapYzNlzvtr9QM8D2UMEIPhX/WLdvPpEEWVzGnD7XpLXjGwfu1ZkJRcXPEZEZ2subh5ZBqOWCFWKv5WwgGYWuYDLHfrBlBgSFWR8cZuyqkmMsWl4CiadXqGA0=</D></RSAKeyValue>";
//            String pubKeyXml3 = FileHelper.readFileText("E:\\rsa\\农行客户密钥5.14\\test_cus_pub.cer","gbk");
//            String pubKeyXml3 = FileHelper.readFileText("C:\\Users\\F\\Desktop\\pubkey.txt","gbk");
//            System.out.println("公钥长度："+pubKeyXml3.length());
//            System.out.println(pubKeyXml3);
//            String priKeyXml3 = FileHelper.readFileText("E:\\rsa\\农行客户密钥5.14\\test_cus_pri.key","gbk");
//            String priKeyXml3 = FileHelper.readFileText("C:\\Users\\F\\Desktop\\prikey.txt","gbk");
//            System.out.println("私钥长度："+priKeyXml3.length());
//            System.out.println(priKeyXml3);
            String pubKeyXml3 ="<RSAKeyValue><Modulus>4mSmuCyxyMLBkOITEJnWGfHwg5NZMM5Ol4lVlwIXFzPn93RSooYQ+6ng8CIT26Q+u75iNnLdQ3mwzjJtpJbbBefES0cyFiMJvN+t79XdvLFZL9yqi3iW3aYvhn3cRKMhe10SMWOS68i2PcspD9E9KAUZyQA5xwdJp3bxE3zID98=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
            String priKeyXml3 ="<RSAKeyValue><Modulus>4mSmuCyxyMLBkOITEJnWGfHwg5NZMM5Ol4lVlwIXFzPn93RSooYQ+6ng8CIT26Q+u75iNnLdQ3mwzjJtpJbbBefES0cyFiMJvN+t79XdvLFZL9yqi3iW3aYvhn3cRKMhe10SMWOS68i2PcspD9E9KAUZyQA5xwdJp3bxE3zID98=</Modulus><Exponent>AQAB</Exponent><P>/JsuoksuTOA8GtqCcPeAJVEJ3SSEpx9YuJks1wFM1W6bX9zihBr/1H0BV9aYFKLgq6+o113uDhCow51IOSLB/w==</P><Q>5W9PxoVtcmiCzcmmEupjZtnGeuhVEniP6LwSs8kTmqwnkwAdeaCSPLww2Fk7GcCk0S+J3Ybz2qn1CfkLaBbyIQ==</Q><DP>SJAjY0eJc1furCAc3GqcixuuY7I70O42REDsGo8rDRyK0YzYHO7MkrOeESd55GOtemQpckRvP+cVM6wYtSEA7Q==</DP><DQ>TYTpgFrOmuxC3cL3EMcO5M5J6HcdX0tK3EjHTOVwcT8fc+SDEPg20LyJCGBFvrK/PUqBm06gj84C0+bIetfd4Q==</DQ><InverseQ>VQFET4ykykud1aySRRLvgyuvoa3n/F/E0KS8j/l9TjQ5mZhJ+koASuYYuA2idSMCJ+3t2zBoiIMOfpzj1IEpLw==</InverseQ><D>OdLhblskEZYE4J+NpQjV0YylFMY22qtnEXdnYAmNokSEtASG8rHxdoX+SPc7QTdjOoq3BRczgn97dD8XGBnb2Nwph7UpCpyd93/4hGthOyYvAQCbUoHpFuXlZofBAxsxVTrzWSmc5nJiQuuTSzSBB+1poWeeSGMUv6/82NIYO0E=</D></RSAKeyValue>";
            System.out.println((new Date()).toLocaleString() + ": 加载公钥中。。。");
            PublicKey pubKey3 = RsaHelper.decodePublicKeyFromXml(pubKeyXml3);
            System.out.println((new Date()).toLocaleString() + ": 加载私钥中。。。");
            PrivateKey priKey3 = RsaHelper.decodePrivateKeyFromXml(priKeyXml3);

//            String dataStr = "Java与.NET和平共处万岁！";
            String dataStr = "东阳市污水处理有限公司         196351...。。。031170\n" +
                    "东阳市污水处理有限公司         196351...。。。031170\n" +
                    "东阳市污水处理有限公司         196351...。。。031170\n";

            byte[] dataByteArray = dataStr.getBytes("gbk");
            System.out.println(dataByteArray.length);
            System.out.println((new Date()).toLocaleString() + ": 加密中。。。"); // 加密
            byte[] encryptedDataByteArray = RsaHelper.encryptData(
                    dataByteArray, pubKey3);

            String encryptedDateStr = Base64Helper.encode(encryptedDataByteArray);
            System.out.println("encryptedData的Base64表示："
                    +encryptedDateStr );

            System.out.println((new Date()).toLocaleString() + ": 解密中。。。"); // 解密

            encryptedDataByteArray = Base64Helper.decode(encryptedDateStr);
            byte[] decryptedDataByteArray = RsaHelper.decryptData(
                    encryptedDataByteArray, priKey3);
            System.out.println("解密后的原文："+new String(decryptedDataByteArray, "gbk"));// 签名



        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void decrypt(String file)throws Exception{
        String encryptedDateStr = FileHelper.readFileText(file,"gbk");
        System.out.println("encryptedData的Base64表示："
                +encryptedDateStr );

        System.out.println((new Date()).toLocaleString() + ": 解密中。。。"); // 解密
        String priKeyXml3 = FileHelper.readFileText("E:\\rsa\\test_bank_pri.key","gbk");

        PrivateKey priKey3 = RsaHelper.decodePrivateKeyFromXml(priKeyXml3);


        byte[] encryptedDataByteArray = Base64Helper.decode(encryptedDateStr);
        byte[] decryptedDataByteArray = RsaHelper.decryptData(
                encryptedDataByteArray, priKey3);
        System.out.println("解密后的原文："+new String(decryptedDataByteArray, "gbk"));


    }
}
